﻿using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using Communication.Message;

namespace Communication.Channel.Serial
{
    /// <summary>
    /// 串口连接助手
    /// </summary>
    internal class DefaultSerialChannel : AbstractChannel, SerialChannel, Channel
    {
        public DefaultSerialChannel(int maxLogMessage)
        {
            this.messageQueue = new MessageQueue(maxLogMessage);
            this.channelConfig = new DefaultSerialChannelConfig();
            this.requestStop = false;
        }

        // Token: 0x0600015C RID: 348 RVA: 0x00003F83 File Offset: 0x00002183
        private void ClearReceiveBuffer()
        {
            this.receiveBufferOffset = 0;
        }

        // Token: 0x0600015D RID: 349 RVA: 0x00003F8C File Offset: 0x0000218C
        public override void Connect(Address remoteAddress)
        {
            if (remoteAddress == null || !(remoteAddress is SerialAddress))
            {
                throw new ArgumentException();
            }
            lock (base.StateLock)
            {
                if (base.ChannelState > AbstractChannel.CHANNEL_STATE_OPEN)
                {
                    throw new InvalidOperationException();
                }
                try
                {
                    this.serialPort = new SerialPort();
                    this.serialPort.PortName = ((SerialAddress)remoteAddress).GetPortName();
                    this.serialPort.BaudRate = this.channelConfig.GetBaudRate();
                    this.serialPort.DataBits = this.channelConfig.GetDataBits();
                    this.serialPort.StopBits = this.channelConfig.GetStopBits();
                    this.serialPort.Parity = this.channelConfig.GetParity();
                    this.serialPort.ReadTimeout = this.channelConfig.GetReadTimeoutMillis();
                    this.serialPort.WriteTimeout = this.channelConfig.GetWriteTimeoutMillis();
                    this.serialPort.ReadBufferSize = this.channelConfig.GetReadBufferSize();
                    this.serialPort.WriteBufferSize = this.channelConfig.GetWriteBufferSize();
                    if (this.serialPort.IsOpen) this.serialPort.Close();
                    this.serialPort.Open();
                    if (!ThreadPool.QueueUserWorkItem(new WaitCallback(this.MessageProc))) //开启一个线程接收数据
                    {
                        if (this.serialPort.IsOpen)
                        {
                            this.serialPort.Close();
                            this.serialPort.Dispose();
                            this.serialPort = null;
                        }
                        throw new Exception("user work item can't queue in thread pool.");
                    }
                }
                catch (Exception innerException)
                {
                    throw new ChannelException("channel connect failed.", innerException);
                }
            }
        }

        // Token: 0x0600015E RID: 350 RVA: 0x0000413C File Offset: 0x0000233C
        public override void Disconnect()
        {
            bool flag = false;
            lock (base.StateLock)
            {
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_CLOSED)
                {
                    return;
                }
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_OPEN)
                {
                    base.ChannelState = AbstractChannel.CHANNEL_STATE_CLOSED;
                    flag = true;
                }
            }
            if (!flag)
            {
                this.requestStop = true;
                if (this.needNotify)
                {
                    lock (this.messageWaitLock)
                    {
                        //Monitor.PulseAll时，所有等待的线程都会被唤醒，并允许它们继续执行
                        Monitor.PulseAll(this.messageWaitLock);
                        this.needNotify = false;
                    }
                }
                return;
            }
            try
            {
                base.NotifyChannelClosedEvent(this);
            }
            catch (Exception error)
            {
                try
                {
                    base.NotifyChannelErrorEvent(this, error);
                }
                catch
                {
                }
            }
        }

        // Token: 0x0600015F RID: 351 RVA: 0x00004218 File Offset: 0x00002418
        public override void Close()
        {
            bool flag = false;
            lock (base.StateLock)
            {
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_CLOSED)
                {
                    return;
                }
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_OPEN)
                {
                    base.ChannelState = AbstractChannel.CHANNEL_STATE_CLOSED;
                    flag = true;
                }
            }
            if (!flag)
            {
                this.requestStop = true;
                if (this.needNotify)
                {
                    lock (this.messageWaitLock)
                    {
                        Monitor.PulseAll(this.messageWaitLock);
                        this.needNotify = false;
                    }
                }
                return;
            }
            try
            {
                base.NotifyChannelClosedEvent(this);
            }
            catch (Exception error)
            {
                try
                {
                    base.NotifyChannelErrorEvent(this, error);
                }
                catch
                {
                }
            }
        }

        // Token: 0x06000160 RID: 352 RVA: 0x000042F4 File Offset: 0x000024F4
        private void MessageProc(object param)
        {
            this.sendBuffer = new byte[this.channelConfig.GetWriteBufferSize()];
            this.receiveBuffer = new byte[this.channelConfig.GetReadBufferSize()];
            this.receiveBufferOffset = 0;
            lock (base.StateLock)
            {
                base.ChannelState = AbstractChannel.CHANNEL_STATE_CONNECTED;
            }
            try
            {
                base.NotifyChannelConnectedEvent(this);
            }
            catch (Exception error)
            {
                try
                {
                    base.NotifyChannelErrorEvent(this, error);
                }
                catch
                {
                }
            }
            while (!this.requestStop) //一直接受数据
            {
                this.currentMessage = this.messageQueue.Dequeue();
                if (this.currentMessage == null)
                {
                    try
                    {
                        lock (this.messageWaitLock)
                        {
                            this.needNotify = true;
                            Monitor.Wait(this.messageWaitLock, 1);
                            this.needNotify = false;
                        }
                        continue;
                    }
                    catch (Exception error2)
                    {
                        try
                        {
                            base.NotifyChannelErrorEvent(this, error2);
                        }
                        catch
                        {
                        }
                        continue;
                    }
                }
                if (this.requestStop)
                {
                    this.CloseChannel();
                    this.requestStop = false;
                }
                else
                {
                    Thread.Sleep(50);
                    try
                    {
                        lock (this.messageProcessLock)
                        {
                            if (this.currentMessage.GetMessageType() == ChannelMessageType.Client)
                            {
                                this.SendData(this.currentMessage);
                                if (!this.requestStop)
                                {
                                    this.ReceiveData(this.currentMessage);
                                    if (this.currentMessage.IsRequestCancel())//判断当前是否已经取消
                                    {
                                        this.SendCancelData(this.currentMessage);
                                        this.currentMessage.SetCancel();
                                    }
                                }
                            }
                            else
                            {
                                this.ReceiveData(this.currentMessage);
                                this.SendData(this.currentMessage);
                            }
                        }
                        continue;
                    }
                    catch (InvalidOperationException)
                    {
                        this.CloseChannel();
                    }
                    catch (IOException)
                    {
                        this.CloseChannel();
                    }
                    catch (Exception failure)
                    {
                        try
                        {
                            this.currentMessage.SetFailure(failure);
                        }
                        catch (Exception error3)
                        {
                            try
                            {
                                base.NotifyChannelErrorEvent(this, error3);
                            }
                            catch
                            {
                            }
                        }
                        continue;
                    }
                }
            IL_1E2:
                this.CloseChannel();
                return;
            }
            this.CloseChannel();
            this.requestStop = false;
            //goto IL_1E2;
        }

        // Token: 0x06000161 RID: 353 RVA: 0x00004580 File Offset: 0x00002780
        private void SendData(ChannelMessage msg)
        {
            long num = DateTime.Now.Ticks / 10000L;
            long num2 = (long)msg.GetSendTimeoutMillis();
            bool flag = true;
            int i = 0;
            this.serialPort.DiscardOutBuffer();
            this.serialPort.DiscardInBuffer();
            this.ClearReceiveBuffer();
            i = msg.GetSendData(this.sendBuffer, 0);
            while (i > 0)
            {
                if (this.requestStop)
                {
                    return;
                }
                if (flag)
                {
                    flag = false;
                }
                else
                {
                    num2 = (long)msg.GetSendTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num);
                    if (num2 <= 0L)
                    {
                        throw new TimeoutException(); //超时异常
                    }
                }
                try
                {
                    this.serialPort.Write(this.sendBuffer, 0, i);
                    i = msg.GetSendData(this.sendBuffer, 0);
                }
                catch (TimeoutException)
                {
                    num2 = (long)msg.GetSendTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num);
                    if (num2 <= 0L)
                    {
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// 发送取消报文
        /// </summary>
        /// <param name="msg"></param>
        /// <exception cref="TimeoutException"></exception>
        private void SendCancelData(ChannelMessage msg)
        {
            long num = DateTime.Now.Ticks / 10000L;
            long num2 = (long)msg.GetSendTimeoutMillis();
            bool flag = true;
            int i = 0;
            this.serialPort.DiscardOutBuffer();
            i = msg.GetCancelSendData(this.sendBuffer, 0);
            while (i > 0)
            {
                if (this.requestStop)
                {
                    return;
                }
                if (flag)
                {
                    flag = false;
                }
                else
                {
                    num2 = (long)msg.GetSendTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num);
                    if (num2 <= 0L)
                    {
                        throw new TimeoutException();
                    }
                }
                try
                {
                    this.serialPort.Write(this.sendBuffer, 0, i);
                    i = msg.GetCancelSendData(this.sendBuffer, 0);
                }
                catch (TimeoutException)
                {
                    num2 = (long)msg.GetSendTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num);
                    if (num2 <= 0L)
                    {
                        throw;
                    }
                }
            }
        }
        /// <summary>
        /// 处理接收数据函数
        /// </summary>
        /// <param name="msg"></param>
        private void ReceiveData(ChannelMessage msg)
        {
            long num = DateTime.Now.Ticks / 10000L;
            long num2 = (long)msg.GetTotalReceiveTimeoutMillis();
            long num3 = DateTime.Now.Ticks / 10000L;
            long num4 = (long)msg.GetSpaceReceiveTimeoutMillis();
            bool flag = true;
            while (!msg.IsDone() && !msg.IsRequestCancel())
            {
                try
                {
                    if (this.requestStop)
                    {
                        break;
                    }
                    if (flag)
                    {
                        flag = false;
                    }
                    else
                    {
                        num2 = (long)msg.GetTotalReceiveTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num);
                        num4 = (long)msg.GetSpaceReceiveTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num3);
                        if (num2 <= 0L || num4 <= 0L)
                        {
                            throw new TimeoutException();
                        }
                    }
                    int bytesToRead = this.serialPort.BytesToRead;
                    if (bytesToRead > 0 || this.receiveBufferOffset > 0)
                    {
                        if (bytesToRead > 0)
                        {
                            if (bytesToRead > this.receiveBuffer.Length - this.receiveBufferOffset)
                            {
                                byte[] sourceArray = this.receiveBuffer;
                                this.receiveBuffer = new byte[bytesToRead + this.receiveBufferOffset];
                                Array.Copy(sourceArray, this.receiveBuffer, this.receiveBufferOffset);
                            }
                            this.receiveBufferOffset += this.serialPort.Read(this.receiveBuffer, this.receiveBufferOffset, bytesToRead);
                            num3 = DateTime.Now.Ticks / 10000L;
                            num4 = (long)msg.GetSpaceReceiveTimeoutMillis();
                        }
                        try
                        {
                            int num5 = msg.FillReceivedData(this.receiveBuffer, 0, this.receiveBufferOffset);//接收数据Buffer
                            this.receiveBufferOffset -= num5;
                            Array.Copy(this.receiveBuffer, num5, this.receiveBuffer, 0, this.receiveBufferOffset);
                            continue;
                        }
                        catch (Exception failure)
                        {
                            try
                            {
                                msg.SetFailure(failure);
                            }
                            catch (Exception error)
                            {
                                try
                                {
                                    base.NotifyChannelErrorEvent(this, error);
                                }
                                catch
                                {
                                }
                            }
                            continue;
                        }
                    }
                    int num6 = this.serialPort.ReadByte();
                    if (num6 >= 0)
                    {
                        this.receiveBuffer[this.receiveBufferOffset] = (byte)num6;
                        this.receiveBufferOffset++;
                        num3 = DateTime.Now.Ticks / 10000L;
                        num4 = (long)msg.GetSpaceReceiveTimeoutMillis();
                    }
                }
                catch (TimeoutException ex)
                {
                    num2 = (long)msg.GetTotalReceiveTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num);
                    num4 = (long)msg.GetSpaceReceiveTimeoutMillis() - (DateTime.Now.Ticks / 10000L - num3);
                    if (num2 <= 0L || num4 <= 0L)
                    {
                        throw;
                    }
                }
            }
        }

        // Token: 0x06000164 RID: 356 RVA: 0x00004A54 File Offset: 0x00002C54
        private void CloseChannel()
        {
            this.requestStop = false;
            lock (base.StateLock)
            {
                if (base.ChannelState != AbstractChannel.CHANNEL_STATE_CONNECTED)
                {
                    return;
                }
            }
            if (this.serialPort != null)
            {
                try
                {
                    try
                    {
                        if (this.serialPort.IsOpen)
                        {
                            this.serialPort.Close();
                        }
                    }
                    catch (Exception error)
                    {
                        try
                        {
                            base.NotifyChannelErrorEvent(this, error);
                        }
                        catch
                        {
                        }
                    }
                    finally
                    {
                        this.serialPort.Dispose();
                        this.serialPort = null;
                    }
                    this.sendBuffer = null;
                    this.receiveBuffer = null;
                    if (this.currentMessage == null)
                    {
                        goto IL_E7;
                    }
                    try
                    {
                        this.currentMessage.SetFailure(new ChannelClosedException());
                        this.currentMessage = null;
                        goto IL_E7;
                    }
                    catch (Exception error2)
                    {
                        try
                        {
                            base.NotifyChannelErrorEvent(this, error2);
                        }
                        catch
                        {
                        }
                        goto IL_E7;
                    }
                    try
                    {
                    IL_BD:
                        this.currentMessage.SetFailure(new ChannelClosedException());
                        this.currentMessage = null;
                    }
                    catch (Exception error3)
                    {
                        try
                        {
                            base.NotifyChannelErrorEvent(this, error3);
                        }
                        catch
                        {
                        }
                    }
                IL_E7:
                    if ((this.currentMessage = this.messageQueue.Dequeue()) != null)
                    {
                        //goto IL_BD;
                    }
                    this.messageQueue = null;
                }
                catch (Exception error4)
                {
                    try
                    {
                        base.NotifyChannelErrorEvent(this, error4);
                    }
                    catch
                    {
                    }
                }
            }
            lock (base.StateLock)
            {
                if (base.ChannelState != AbstractChannel.CHANNEL_STATE_CONNECTED)
                {
                    return;
                }
                base.ChannelState = AbstractChannel.CHANNEL_STATE_CLOSED;
            }
            try
            {
                base.NotifyChannelClosedEvent(this);
            }
            catch (Exception error5)
            {
                try
                {
                    base.NotifyChannelErrorEvent(this, error5);
                }
                catch
                {
                }
            }
        }

        // Token: 0x06000165 RID: 357 RVA: 0x00004C68 File Offset: 0x00002E68
        public override ChannelConfig GetConfig()
        {
            return this.channelConfig;
        }

        // Token: 0x06000166 RID: 358 RVA: 0x00004C70 File Offset: 0x00002E70
        public override void WriteAsync(ChannelMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException();
            }
            lock (base.StateLock)
            {
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_OPEN)
                {
                    throw new ChannelUnconnectedException();
                }
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_CLOSED)
                {
                    throw new ChannelClosedException();
                }
                try
                {
                    this.messageQueue.Enqueue(message); //设置数据Queue
                }
                catch (MessageQueueFullException)
                {
                    throw new ChannelBusyException();
                }
                if (this.needNotify)
                {
                    lock (this.messageWaitLock)
                    {
                        Monitor.PulseAll(this.messageWaitLock);
                        this.needNotify = false;
                    }
                }
            }
        }

        // Token: 0x06000167 RID: 359 RVA: 0x00004D34 File Offset: 0x00002F34
        public override void WriteSync(ChannelMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException();
            }
            lock (base.StateLock)
            {
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_OPEN)
                {
                    throw new ChannelUnconnectedException();
                }
                if (base.ChannelState == AbstractChannel.CHANNEL_STATE_CLOSED)
                {
                    throw new ChannelClosedException();
                }
            }
            try
            {
                try
                {
                    lock (this.messageProcessLock)
                    {
                        if (message.GetMessageType() == ChannelMessageType.Client)
                        {
                            this.SendData(message);
                            this.ReceiveData(message);
                        }
                        else
                        {
                            this.ReceiveData(message);
                            this.SendData(message);
                        }
                    }
                }
                catch (Exception ex)
                {
                    try
                    {
                        if (ex is InvalidOperationException || ex is IOException)
                        {
                            message.SetFailure(new ChannelClosedException());
                        }
                        else
                        {
                            message.SetFailure(ex);
                        }
                    }
                    catch (Exception error)
                    {
                        try
                        {
                            base.NotifyChannelErrorEvent(this, error);
                        }
                        catch
                        {
                        }
                    }
                    throw;
                }
            }
            catch (InvalidOperationException)
            {
                this.Close();
            }
            catch (IOException)
            {
                this.Close();
            }
            catch (Exception)
            {
            }
        }

        // Token: 0x04000051 RID: 81
        private MessageQueue messageQueue;

        // Token: 0x04000052 RID: 82
        private SerialPort serialPort;

        // Token: 0x04000053 RID: 83
        private DefaultSerialChannelConfig channelConfig;

        // Token: 0x04000054 RID: 84
        private volatile bool requestStop;

        // Token: 0x04000055 RID: 85
        private object messageWaitLock = new object();

        // Token: 0x04000056 RID: 86
        private volatile bool needNotify;

        // Token: 0x04000057 RID: 87
        private object messageProcessLock = new object();

        // Token: 0x04000058 RID: 88
        private ChannelMessage currentMessage;

        // Token: 0x04000059 RID: 89
        private byte[] sendBuffer;

        // Token: 0x0400005A RID: 90
        private byte[] receiveBuffer;

        // Token: 0x0400005B RID: 91
        private int receiveBufferOffset;
    }
}
